#include "clientsocket.h"

#include <QTcpSocket>
#include <QDateTime>
#include <QDebug>

#include "confdeal.h"
//
ClientSocket::ClientSocket(QObject *parent)
    : QThread(parent)
    , running(true)
    , m_ip("127.0.0.1")
    , m_port(60009)
    , m_csocket(NULL)
    , linking(false)
    , ativity(false)
    , limitSize(10)
{
    ptr_ConfDeal = ConfDeal::getInstance();
    m_ip = ptr_ConfDeal->getSrvIP();
    m_port = ptr_ConfDeal->getSrvPort();
    ativityCheck = this->getSec();
    rx = QRegExp("\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b");
}

ClientSocket::~ClientSocket()
{
    running=false;
    try{
        this->terminate();
        this->wait();
        qDebug() << "ClientSocketThread::destroy\n";
    }catch(...){
        qDebug() << "ClientSocketThread::destroy fail\n";
    }
}

void ClientSocket::initIpForView()
{
    emit ipLinkSuccess(QVariant(m_ip));
}

void ClientSocket::run()
{
    initTcpSocket();
    qRegisterMetaType<QAbstractSocket::SocketError>("MySocketError");
    while (running) {
        this->checkLink();
        this->doCmd();
        this->checkForReadyRead();
        this->msleep(10);
    }
    destroyTcpSocket();
    exec();
};

bool ClientSocket::initTcpSocket()
{
    try{
        if(NULL==m_csocket){
            m_csocket = new QTcpSocket();
            m_csocket->moveToThread(this);
            connect(m_csocket,SIGNAL(readyRead()),this,SLOT(readData()));
            connect(m_csocket,SIGNAL(error(QAbstractSocket::SocketError))
                    ,this,SLOT(sockeError(QAbstractSocket::SocketError)));
            return true;
        }
        return false;
    }catch(...){
        qDebug() << "ClientSocket::initTcpSocket Exception";
        return false;
    }
}

bool ClientSocket::destroyTcpSocket()
{
    try{
        if(NULL!=m_csocket){
            disconnect(m_csocket,SIGNAL(readyRead()),this,SLOT(readData()));
            disconnect(m_csocket,SIGNAL(error(QAbstractSocket::SocketError))
                    ,this,SLOT(sockeError(QAbstractSocket::SocketError)));
            linking = !disconnectMyHost();
            delete m_csocket;
            m_csocket = NULL;
            return true;
        }
        return false;
    }catch(...){
        qDebug() << "ClientSocket::destroyTcpSocket Exception";
        return false;
    }
}

bool ClientSocket::connectMyHost()
{
    try{
        if(NULL==m_csocket){
            return false;
        }
        if(QAbstractSocket::UnconnectedState==m_csocket->state()){
            m_csocket->connectToHost(m_ip,m_port);
            if(m_csocket->waitForConnected(2000)){
                qDebug() << QString("success connect to host(%1:%2)")
                            .arg(m_ip).arg(m_port);
                ativity = true;
                emit ipLinkSuccess(QVariant(m_ip));
                return true;
            }else{
                qDebug() << QString("connect to host(%1:%2) fail!")
                            .arg(m_ip).arg(m_port);
                return false;
            }
        }else if(QAbstractSocket::ConnectedState==m_csocket->state()){
            return true;
        }else{
            return false;
        }
    }catch(...){
        qDebug() << "ClientSocket::connectMyHost Exception";
        return false;
    }
}

bool ClientSocket::disconnectMyHost()
{
    try{
        if(NULL==m_csocket){
            return true;
        }
        if(QAbstractSocket::ConnectedState==m_csocket->state()){
            m_csocket->disconnectFromHost();
            qDebug() << QString("disconnect to host(%1:%2)")
                        .arg(m_ip).arg(m_port);
            return m_csocket->waitForDisconnected();
        }else if(QAbstractSocket::UnconnectedState==m_csocket->state()){
            return true;
        }else{
            return false;
        }
    }catch(...){
        qDebug() << "ClientSocket::disconnectMyHost Exception";
        return true;
    }
}

void ClientSocket::sockeError(QAbstractSocket::SocketError error)
{
    try{
        if(NULL==m_csocket)
            return;
        switch (error) {
        case QAbstractSocket::ConnectionRefusedError:
            break;
        case QAbstractSocket::RemoteHostClosedError:
            break;
        case QAbstractSocket::HostNotFoundError:
            break;
        case QAbstractSocket::SocketAccessError:
            break;
        case QAbstractSocket::SocketResourceError:
            break;
        case QAbstractSocket::SocketTimeoutError:
            return;//for waitForReadyRead can set sleep time
        case QAbstractSocket::DatagramTooLargeError:
            break;
        case QAbstractSocket::NetworkError:
            break;
        case QAbstractSocket::AddressInUseError:
            break;
        case QAbstractSocket::SocketAddressNotAvailableError:
            break;
        case QAbstractSocket::UnsupportedSocketOperationError:
            break;
        case QAbstractSocket::ProxyAuthenticationRequiredError:
            break;
        case QAbstractSocket::SslHandshakeFailedError:
            break;
        case QAbstractSocket::UnfinishedSocketOperationError:
            break;
        case QAbstractSocket::ProxyConnectionRefusedError:
            break;
        case QAbstractSocket::ProxyConnectionClosedError:
            break;
        case QAbstractSocket::ProxyConnectionTimeoutError:
            break;
        case QAbstractSocket::ProxyNotFoundError:
            break;
        case QAbstractSocket::ProxyProtocolError:
            break;
        case QAbstractSocket::OperationError:
            break;
        case QAbstractSocket::SslInternalError:
            break;
        case QAbstractSocket::SslInvalidUserDataError:
            break;
        case QAbstractSocket::TemporaryError:
            break;
        case QAbstractSocket::UnknownSocketError:
            break;
        default:
            break;
        }
        qDebug() << QString("socket(%2,%3) error:"+m_csocket->errorString())
                    .arg(m_ip).arg(m_port);
        linking = !disconnectMyHost();
    }catch(...){
        qDebug() << "ClientSocket::sockeError Exception";
        linking = false;
    }
}

void ClientSocket::readData()
{
    if(NULL==m_csocket)
        return;
    if(QAbstractSocket::ConnectedState!=m_csocket->state())
        return;
    while(m_csocket->bytesAvailable())
    {
        QByteArray vTemp;
        try{
            vTemp = m_csocket->readAll();
            ativity = true;
//            qDebug()<<QString("readData(%1)").arg(vTemp.length());
        }catch (...)
        {
            qDebug() << QString("m_csocket readAll Exception,[%1 %2 %3]!")
                        .arg(__FILE__).arg(__FUNCTION__).arg(__LINE__);
            break;
        }
        try
        {
            unsigned char *buff = (unsigned char*)vTemp.data();
            int start = 0;
            unsigned char ctype = 0;
            for (int i = 0; i < vTemp.length(); i++)
            {
                //printf_s("%02X ",buff[i]);
                if (buff[i] > 0xf0) {
                    if (buff[i] == 0xff) {
                        if (ctype) {
                            ctype = 0;
                            readCache((const unsigned char *)buff + start,i - start + 1);
                            start = i + 1;
                        }
                    }
                    else {
                        ctype = buff[i];
                        start = i;
                    }
                }
            }
            //printf_s("\n");
            buff = NULL;
            if (start < vTemp.length())
            {
                printf("Exception for vTemp.length()\n");
            }
        }
        catch (...)
        {
            qDebug() << QString("NetGatherDev Exception false,[%1 %2 %3]!")
                        .arg(__FILE__).arg(__FUNCTION__).arg(__LINE__);
        }
    }
}

void ClientSocket::readCache(const unsigned char *bufdata,int buflen)
{
    try{
        unsigned char * pBuf = new unsigned char[buflen];
        int nLen = uncode(bufdata, buflen, pBuf);
        unsigned char type = pBuf[0];
        int idx = 1;
        switch (type)
        {
        case 0XF3:
        {
            int len = pBuf[idx++];
            len += (pBuf[idx++] << 8);
            //devid
            int devID = 0;
            devID += pBuf[idx++];
            devID += (pBuf[idx++] << 8);
            devID += (pBuf[idx++] << 16);
            devID += (pBuf[idx++] << 24);
            //devtype
            int devType = 0;
            devType += pBuf[idx++];
            devType += (pBuf[idx++] << 8);
            devType += (pBuf[idx++] << 16);
            devType += (pBuf[idx++] << 24);
            //name && desc
            QString name = QString::fromLocal8Bit((char*)(pBuf+idx),nLen-12);
            //
            unsigned char endflag = pBuf[nLen-1];
            if(0XFF!=endflag){
                qDebug() << QString("dataReceived error end,len(%1)").arg(len+1);
            }else{
//                qDebug() << QString("addDev(%1,%2,%3,%4)").arg(devID).arg(devType).arg(name).arg(len);
                emit addDev(devID,devType,name,name);
            }
        }
            break;
        case 0XF4:
        {
            int len = pBuf[idx++];
            len += (pBuf[idx++] << 8);
            //devid
            int devID = 0;
            devID += pBuf[idx++];
            devID += (pBuf[idx++] << 8);
            devID += (pBuf[idx++] << 16);
            devID += (pBuf[idx++] << 24);
            //pid
            int pID = 0;
            pID += pBuf[idx++];
            pID += (pBuf[idx++] << 8);
            pID += (pBuf[idx++] << 16);
            pID += (pBuf[idx++] << 24);
            //ptype
            int pType = 0;
            pType += pBuf[idx++];
            pType += (pBuf[idx++] << 8);
            pType += (pBuf[idx++] << 16);
            pType += (pBuf[idx++] << 24);
            //
            float defValue = float(0.0);
            unsigned char* fval = (unsigned char*)&defValue;
            fval[3] = pBuf[idx++];
            fval[2] = pBuf[idx++];
            fval[1] = pBuf[idx++];
            fval[0] = pBuf[idx++];
            fval = NULL;
            //name && desc
            QString name = QString::fromLocal8Bit((char*)(pBuf+idx),nLen-20);
            //
            unsigned char endflag = pBuf[nLen-1];
            if(0XFF!=endflag){
                qDebug() << QString("dataReceived error end,len(%1)").arg(len+1);
            }else{
//                qDebug() << QString("addPInfo(%1,%2,%3,%4,%5)")
//                            .arg(devID).arg(pID).arg(pType).arg(name).arg(defValue, 0, 'f', 2);
                emit addPInfo(devID,pID,name,name,pType,QString("%1").arg(defValue, 0, 'f', 2));
            }
        }
            break;
        case 0XF5:
        {
            int len = pBuf[idx++];
            len += (pBuf[idx++] << 8);
            //devid
            int devID = 0;
            devID += pBuf[idx++];
            devID += (pBuf[idx++] << 8);
            devID += (pBuf[idx++] << 16);
            devID += (pBuf[idx++] << 24);
            //pID
            int pID = 0;
            pID += pBuf[idx++];
            pID += (pBuf[idx++] << 8);
            pID += (pBuf[idx++] << 16);
            pID += (pBuf[idx++] << 24);
            //sc
            int sc = 0;
            sc += pBuf[idx++];
            sc += (pBuf[idx++] << 8);
            sc += (pBuf[idx++] << 16);
            sc += (pBuf[idx++] << 24);
            //msc
            int msc = 0;
            msc += pBuf[idx++];
            msc += (pBuf[idx++] << 8);
            msc += (pBuf[idx++] << 16);
            msc += (pBuf[idx++] << 24);
            //val
            float value = float(0.0);
            unsigned char* fval = (unsigned char*)&value;
            fval[3] = pBuf[idx++];
            fval[2] = pBuf[idx++];
            fval[1] = pBuf[idx++];
            fval[0] = pBuf[idx++];
            fval = NULL;
            //
            unsigned char endflag = pBuf[nLen-1];
            if(0XFF!=endflag){
                qDebug() << QString("dataReceived error end,len(%1)").arg(len+1);
            }else{
                if(devID<0&&pID<0){
                    ;
                }else{
                    QString dt_str = getDTStr(sc,msc);
//                    qDebug() << QString("PValue(%1,%2,%3(%4,%5),%6)")
//                                .arg(devID).arg(pID).arg(dt_str).arg(sc).arg(msc)
//                                .arg(value, 0, 'f', 2);
                    emit PValue(devID,pID,dt_str,QString("%1").arg(value, 0, 'f', 2));
                }
            }
        }
            break;
        default:
            break;
        }
        delete [] pBuf;
        pBuf = NULL;
    }catch(...){
        qDebug() << "ClientSocket::readCache Exception";
    }
}

unsigned int ClientSocket::getUsec()
{
    return static_cast<unsigned int>(QDateTime::currentMSecsSinceEpoch());
}

unsigned int ClientSocket::getSec()
{
    return static_cast<unsigned int>(QDateTime::currentSecsSinceEpoch());
}

QString ClientSocket::getDTStr(int sc, int msc)
{
    return QDateTime::fromTime_t(sc).addMSecs(msc).toString("MM-dd hh:mm:ss.zzz");
}

int ClientSocket::code(const unsigned char *buff, const int len, unsigned char *outbuf)
{
    try{
        char ch = 0;
        int nLen = 0;
        unsigned char * buf = (unsigned char *)buff;
        for (int i = 0; i < len; i++, nLen++)
        {
            ch = buf[i];
            if ((buf[i] | 0x0f) == 0xff && i > 0 && i < (len - 1))
            {
                *outbuf++ = 0xf0 & buf[i];
                *outbuf++ = 0x0f & buf[i];
                nLen += 1;
            }
            else {
                *outbuf++ = ch;
            }
        }
        buf = NULL;
        return nLen;
    }catch(...){
        return 0;
    }
}

int ClientSocket::uncode(const unsigned char *buff, int len, unsigned char *outbuf)
{
    try{
        char ch = 0;
        int nLen = 0;
        unsigned char * buf = (unsigned char *)buff;
        for (int i = 0; i < len; i++, nLen++)
        {
            ch = buf[i];
            if (buf[i] == 0xf0)
            {
                if (i > len - 2)
                    printf("Error!\r\n");
                if (buf[i + 1] > 0x0f)
                    printf("Error!\r\n");
                ch = 0xf0 | buf[++i];
            }
            *outbuf++ = ch;
        }
        buf = NULL;
        return nLen;
    }catch(...){
        return 0;
    }
};

void ClientSocket::setPValue(int devID,int pID,qreal val)
{
    if(qIsNaN(val)){
        qDebug()<< QString("setPValue exception for devID:%1,pID:%2,val:%3")
                   .arg(devID).arg(pID).arg(val);
        return;
    }
    DownCmd _cmd(devID,pID,val);
    addCmd(_cmd);
}

void ClientSocket::onIpConf(QString ip_)
{
    if(!rx.exactMatch(ip_))
    {
        qDebug()<< QString("warning format for IP:%1").arg(ip_);
        return;
    }
    qDebug() << "old ip=" <<m_ip;
    m_ip = ip_;
    qDebug() << "set ip=" <<ip_;
    ptr_ConfDeal->setSrvIP(ip_);
    ativityCheck-=10;
    qDebug() << "new ip=" <<ip_;
}

void ClientSocket::checkLink()
{
    try{
        if(!linking){
            linking = connectMyHost();
        }else{
            if((ativityCheck+10)<this->getSec())
            {
                if(!ativity){
                    linking = !disconnectMyHost();
                }
                ativityCheck = this->getSec();
                ativity = false;
            }
        }
    }catch(...){
        qDebug() << "ClientSocket::checkLink Exception";
    }
}

void ClientSocket::checkForReadyRead()
{
    if(NULL!=m_csocket&&linking)
    {
        m_csocket->waitForReadyRead(100);
    }
}

void ClientSocket::doCmd()
{
    DownCmd _cmd;
    if(getCmd(_cmd))
    {
        Control(_cmd.devID,_cmd.pID,_cmd.val);
    }
}

bool ClientSocket::getCmd(DownCmd &cmd_)
{
    bool ret = false;
    downcmds_mutex.lock();
    if(!downcmds.isEmpty())
    {
        cmd_ = downcmds.dequeue();
        ret = true;
    }
    downcmds_mutex.unlock();
    return ret;
}

void ClientSocket::addCmd(DownCmd cmd_)
{
    downcmds_mutex.lock();
    if(downcmds.size()>limitSize)
    {
        downcmds.dequeue();
    }
    downcmds.enqueue(cmd_);
    downcmds_mutex.unlock();
}

bool ClientSocket::downcmdsEmpty()
{
    bool ret = false;
    downcmds_mutex.lock();
    ret = downcmds.isEmpty();
    downcmds_mutex.unlock();
    return ret;
}

void ClientSocket::Control(int devID,int pID,qreal val)
{
    if(NULL==m_csocket)
        return;
    if(QAbstractSocket::ConnectedState!=m_csocket->state())
        return;
//    qDebug() << QString("ClientSocket::setPValue(%1,%2,%3)")
//                .arg(devID).arg(pID).arg(val,0,'f',2);
    try{
        int idx = 0;
        unsigned char buf[512] = {0};
        buf[idx++]=0XF6;
        //信息长度
        buf[idx++] = 0xf0;		//1-Len_L
        buf[idx++] = 0xf0;		//2-Len_H
        //devid
        buf[idx++] = (unsigned char)(devID & 0xff);
        buf[idx++] = (unsigned char)((devID >> 8) & 0xff);
        buf[idx++] = (unsigned char)((devID >> 16) & 0xff);
        buf[idx++] = (unsigned char)((devID >> 24) & 0xff);
        //pid
        buf[idx++] = (unsigned char)(pID & 0xff);
        buf[idx++] = (unsigned char)((pID >> 8) & 0xff);
        buf[idx++] = (unsigned char)((pID >> 16) & 0xff);
        buf[idx++] = (unsigned char)((pID >> 24) & 0xff);
        //val
        float _val = val;
        unsigned char*vavch = (unsigned char*)&_val;
        buf[idx++] = vavch[3];
        buf[idx++] = vavch[2];
        buf[idx++] = vavch[1];
        buf[idx++] = vavch[0];
        vavch = NULL;
        buf[1] = static_cast<unsigned char>(idx & 0xff);
        buf[2] = static_cast<unsigned char>((idx >> 8) & 0xff);
        unsigned char m_end = 0xFF;
        memcpy(buf+idx,&m_end,1);
        idx++;

        unsigned char _buf[256]={0};
        int nLen = code(buf, idx, _buf);
    //    QByteArray _wd((const char*)_buf,nLen);
    //    for(int j=0; j<nLen;j++){
    //        printf("%02X",_buf[j]);
    //    }
    //    printf("\n");
        m_csocket->write((const char*)_buf, nLen);
        ativity = m_csocket->waitForBytesWritten();
        if(!ativity){
            qDebug() << "ClientSocket::Control for writing error!";
        }
    //    ativity = m_csocket->flush();
    }catch(...){
        qDebug() << "ClientSocket::Control Exception";
    }
}
